/*
 * @(#)JaroFunction.java
 *
 * Revision:
 * Author                                         Date           
 * --------------------------------------------   ------------   
 * Ana Emilia Victor Barbosa Coutinho             25/09/2013    
 */
package br.edu.ufcg.splab.techniques.reduction.functions.similarity;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.core.edges.Edge;
import br.edu.ufcg.splab.generator.TestCase;


public class JaroFunction implements DistanceFunction{

	/**
	 * Calculates the similarity score of objects, where 0.0 implies absolutely no similarity
	 * and 1.0 implies absolute similarity.
	 *
	 * @param first The first string to compare.
	 * @param second The second string to compare.
	 * @return A number between 0.0 and 1.0.
	 */
	public double getSimilarity(TestCase first, TestCase second) {
		TestCase shorter;
		TestCase longer;

		// Determine which String is longer.
		if (first.getTestCase().size() > second.getTestCase().size()) {
			longer = first;
			shorter = second;
		} else {
			longer = second;
			shorter = first;
		}

		// Calculate the half length() distance of the shorter String.
		int halflength = (shorter.getTestCase().size() / 2) + 1;
        // Find the set of matching characters between the shorter and longer strings. Note that
		// the set of matching characters may be different depending on the order of the strings.
		TestCase m1 = getSetOfMatchingCharacterWithin(shorter, longer, halflength);
		TestCase m2 = getSetOfMatchingCharacterWithin(longer, shorter, halflength);
		
		// If one or both of the sets of common characters is empty, then
		// there is no similarity between the two strings.
		if (m1.getTestCase().size() == 0 || m2.getTestCase().size() == 0) return 0.0;

		// If the set of common characters is not the same size, then
		// there is no similarity between the two strings, either.
		if (m1.getTestCase().size() != m2.getTestCase().size()) return 0.0;

		// Calculate the number of transpositions between the two sets
		// of common characters.
		int transpositions = transpositions(m1, m2);
		// Calculate the distance.
		System.out.println("t: " + transpositions);
		System.out.println("m: " + m1.getTestCase().size());
		double dist =
				(m1.getTestCase().size() / ((double)shorter.getTestCase().size()) +
						m2.getTestCase().size() / ((double)longer.getTestCase().size()) +
						(m1.getTestCase().size() - transpositions) / ((double)m1.getTestCase().size())) / 3.0;
		return dist;
	}

	/**
     * Provide a string representation of the similarity function to be written.
     * @return The string representation of the similarity function.
     */
	public String getFunctionName(){
		return "Jaro";
	}
	
	/**
	 * Gets a set of matching characters between two strings.
	 *
	 * @param first The first string.
	 * @param second The second string.
	 * @param limit The maximum distance to consider.
	 * @return A string contain the set of common characters.
	 * @remarks Two characters from the first string and the second string are considered matching if the character's
	 * respective positions are no farther than the limit value.
	 */
	private TestCase getSetOfMatchingCharacterWithin(TestCase first, TestCase second, int limit)	{

		TestCase common = new TestCase();
		TestCase copy =new TestCase();
		for(InterfaceEdge e:second.getTestCase())
			copy.addEdge(e);
		
		for (int i = 0; i < first.getTestCase().size(); i++) {
			InterfaceEdge ch = first.getTestCase().get(i);
			boolean found = false;

			// See if the character is within the limit positions away from the original position of that character.
			for (int j = Math.max(0, i - limit); !found && j < Math.min(i + limit, second.getTestCase().size()); j++){
				if (copy.getTestCase().get(j).getLabel().equals(ch.getLabel())){
					found = true;
					common.addEdge(ch);
					InterfaceEdge element = new Edge(copy.getTestCase().get(j).getFrom(), "*", copy.getTestCase().get(j).getTo(), copy.getTestCase().get(j).getType());
					copy.getTestCase().set(j, element);
				}
			}
		}
		return common;
	}

	/**
	 * Calculates the number of transpositions between two strings.
	 * @param first The first string.
	 * @param second The second string.
	 * @return The number of transpositions between the two strings.
	 */
	private int transpositions(TestCase first, TestCase second){
		int transpositions = 0;
		for (int i = 0; i < first.getTestCase().size(); i++){
			if (!first.getTestCase().get(i).equals(second.getTestCase().get(i))){
				transpositions++;
			}
		}
		transpositions /= 2;
		return transpositions;
	}
	
}